package msg.servlet;

import java.net.*;
import java.sql.SQLException;
import java.util.Collection;

import javax.annotation.Resource;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.Status;

import msg.*;
import msg.xml.XMLTransformer;

import org.json.*;
import org.w3c.dom.*;

import user.Session;
import user.servlet.UsersResource;

import common.exceptions.InvalidKeyException;
import common.extensions.Extender;
import common.servlet.*;

@Resource
@Path("/messages")
@SuppressWarnings({"javadoc", "static-method"})
public class MessageResource extends AbstractResource {

	@POST
	public Response post(JSONObject object, @CookieParam(value = UsersResource.COOKIE_NAME) Long sessionId) {
		try (ManagersBundle mb = new ManagersBundle()) {
			if (sessionId == null) {
				return missingCookie();
			}
			String content = object.getString("content");
			JSONObject location = object.getJSONObject("location");
			Location loc = new Location(location);
			String userId = mb.lm.getSession(sessionId).getUser().getId();
			Message msg = mb.mm.createNewMessage(loc, content, mb.lm.get(userId));
			mb.commit();
			return Response.created(new URI(msg.getId().toString())).build();
		} catch (JSONException e) {
			return handleJsonException(e);
		} catch (InvalidKeyException e) {
			return missingCookie();
		} catch (RuntimeException | URISyntaxException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Path("/{id}")
	@Produces(MediaType.APPLICATION_JSON)
	public Response retrieve(@PathParam("id") long messageId) {
		try (ManagersBundle mb = new ManagersBundle()) {
			Message msg = mb.mm.get(messageId);
			mb.commit();
			JSONObject result = toJSON(msg);
			Response response;
			if (isEvent(msg) == false) {
				response = Response.ok().entity(result).build();
			} else {
				response = Response.status(Status.NOT_FOUND).entity("id belongs to an event").build();
			}
			return response;
		} catch (JSONException e) {
			return handleJsonException(e);
		} catch (InvalidKeyException e) {
			return Response.status(Status.NOT_FOUND).build();
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@DELETE
	@Path("/{id}")
	public Response delete(@PathParam("id") Long messageId,
			@CookieParam(value = UsersResource.COOKIE_NAME) Long sessionId) {
		try (ManagersBundle mb = new ManagersBundle()) {
			if (sessionId == null) {
				return missingCookie();
			}
			Session session = mb.lm.getSession(sessionId);
			String userId = session.getUser().getId();
			Message msg;
			try {
				msg = mb.mm.get(messageId);
			} catch (InvalidKeyException e) {
				return Response.noContent().build();
			}
			if (msg.getPoster().getId().equals(userId) == false) {
				return Response.status(Status.FORBIDDEN).entity("you are not the creator of this message")
						.build();
			}

			if (isEvent(msg)) {
				return Response.status(Status.NOT_FOUND).entity("resource is an event").build();
			}
			mb.mm.delete(messageId);
			mb.commit();
			return Response.ok().build();
		} catch (JSONException e) {
			return handleJsonException(e);
		} catch (InvalidKeyException e) {
			return missingCookie();
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Produces(MediaType.APPLICATION_JSON)
	public Response list() {
		try (ManagersBundle mb = new ManagersBundle()) {
			Collection<Message> msgs = mb.mm.getAll();
			mb.commit();
			msgs = Extender.where(msgs, getMessagePredicate());
			JSONArray array = msgsToOrderedArray(msgs);
			return Response.ok().entity(array).build();
		} catch (JSONException e) {
			return handleJsonException(e);
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Path("/inRadius")
	@Produces(MediaType.APPLICATION_JSON)
	public Response listInRadius(@QueryParam("lat") double latCenter, @QueryParam("long") double longCenter,
			@QueryParam("n") double n, @QueryParam("s") double s, @QueryParam("w") double w,
			@QueryParam("e") double e, @QueryParam("since") Long since) {
		try (ManagersBundle mb = new ManagersBundle()) {
			Location center = new Location(latCenter, longCenter);
			Location sw = new Location(w, s);
			Location ne = new Location(e, n);
			double distance = getRadius(center, sw, ne);
			Collection<Message> msgs = mb.mm
					.getAllMessagesInRadius(new Location(latCenter, longCenter), distance, since);
			mb.commit();
			msgs = Extender.where(msgs, getMessagePredicate());
			JSONArray array = msgsToOrderedArray(msgs);
			return Response.ok().entity(array).build();
		} catch (JSONException exp) {
			return handleJsonException(exp);
		} catch (Exception exp) {
			return handleException(exp);
		}
	}

	@GET
	@Path("/xml")
	@Produces(MediaType.APPLICATION_XML)
	public Response toXml(@QueryParam("noEvents") Boolean noEvents) {
		boolean msgsOnly = (noEvents != null) && (noEvents == true);
		try (ManagersBundle mb = new ManagersBundle()) {

			Collection<Message> all = mb.mm.getAll();
			mb.commit();
			Document doc = XMLTransformer.toXML(all);
			Node result = doc;
			if (msgsOnly) {
				result = XMLTransformer.toMessagesOnly(doc);
			}
			return sendXML(result, "messages.xml");
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Path("/kml")
	@Produces(MediaType.TEXT_XML)
	public Response toKml() {
		try (ManagersBundle mb = new ManagersBundle()) {
			Collection<Message> all = mb.mm.getAll();
			mb.commit();
			Document doc = XMLTransformer.toXML(all);
			Node result = XMLTransformer.toMessagesOnly(doc);
			return sendXML(result, "messages.kml");
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Path("/path")
	@Produces(MediaType.TEXT_XML)
	public Response toPath() {
		try (ManagersBundle mb = new ManagersBundle()) {
			Collection<Message> all = mb.mm.getAll();
			mb.commit();
			Document doc = XMLTransformer.toXML(all);
			Node result = XMLTransformer.toMessagesOnly(doc);
			result = XMLTransformer.toKMLPath(result);
			return sendXML(result, "messages_path.xml");
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

	@GET
	@Path("/allPath")
	@Produces(MediaType.TEXT_XML)
	public Response allPaths() {
		try (ManagersBundle mb = new ManagersBundle()) {
			Collection<Message> all = mb.mm.getAll();
			mb.commit();
			Document doc = XMLTransformer.toXML(all);
			Node result = XMLTransformer.toKMLPath(doc);
			return sendXML(result, "all_messages_path.xml");
		} catch (RuntimeException | SQLException e) {
			return handleException(e);
		}
	}

}
